/*
 * Galaxium Messenger
 *
 * Copyright (C) 2007 Paul Burton <paulburton89@gmail.com>
 * Copyright (C) 2007 Philippe Durand <draekz@gmail.com>
 * 
 * License: GNU General Public License (GPL)
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the Free
 * Software Foundation; either version 2 of the License, or (at your option)
 * any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Text;

using Anculus.Core;

using Galaxium.Client;
using Galaxium.Core;
using Galaxium.Protocol;

using Mono.Addins;

namespace Galaxium.Gui
{
	public static class EmoticonUtility
	{
		static List<CustomEmoticonInfo> _customEmoticons;
		static List<IEmoticonFactory> _factories;
		static List<IEmoticonSet> _sets;
		static IEmoticonSet _activeSet;
		static string _cacheDir;
		static string _customDir;

		internal static IConfigurationSection _config = Configuration.Emoticon.Section;
		
		//TODO: cache account/dest dicts based on a weak reference
		//static Dictionary<IEntity, OrderedDictionary<string, IEmoticon>> _cachedDicts = new Dictionary<IEntity, OrderedDictionary<string, IEmoticon>> ();
		static OrderedDictionary<string, IEmoticon> _masterDict;
				
		static EmoticonUtility()
		{
			_customEmoticons = new List<CustomEmoticonInfo>();
			_factories = new List<IEmoticonFactory>();
			_sets = new List<IEmoticonSet>();
			_activeSet = null;
			
			_cacheDir = Path.Combine(CoreUtility.GetConfigurationSubDirectory("Cache"), "Emoticons");
			_customDir = Path.Combine(CoreUtility.GetConfigurationSubDirectory("Cache"), "CustomEmoticons");
		}

		public static List<IEmoticonSet> Sets
		{
			get { return _sets; }
		}
		
		public static IEmoticonSet ActiveSet
		{
			get { return _activeSet; }
			set
			{
				_activeSet = value;
				
				if (_activeSet != null)
					_config.SetString(Configuration.Emoticon.Active.Name, _activeSet.Name);
				else
					_config.SetString(Configuration.Emoticon.Active.Name, string.Empty);
			}
		}
		
		public static List<CustomEmoticonInfo> Custom
		{
			get { return _customEmoticons; }
		}

		public static void Initialize()
		{
			Log.Debug ("Initializing emoticon utility...");
			
			BaseUtility.CreateDirectoryIfNeeded(_cacheDir);
			BaseUtility.CreateDirectoryIfNeeded(_customDir);
			LoadFactories();
			LoadCustomEmoticons();
			AddinManager.ExtensionChanged += OnExtensionChanged;
		}
		
		public static void Shutdown()
		{
			//if (Directory.Exists(_cacheDir))
			//	Directory.Delete(_cacheDir, true);
		}

		static void LoadFactories()
		{
			_factories.Clear();
			
			foreach (EmoticonFactoryExtension node in AddinUtility.GetExtensionNodes("/Galaxium/Themes/Emoticon"))
			{
				IEmoticonFactory factory = node.GetFactory();
				factory.Initialize(_cacheDir);
				_factories.Add(factory);
			}
			
			LoadSets();
		}

		static void LoadSets()
		{
			_sets.Clear();
			_activeSet = null;
			
			foreach (IEmoticonFactory factory in _factories)
			{
				foreach (IEmoticonSet emoticonset in factory.Sets)
				{
					_sets.Add(emoticonset);
				
					if (emoticonset.Name == _config.GetString(Configuration.Emoticon.Active.Name, Configuration.Emoticon.Active.Default))
						_activeSet = emoticonset;
				}
			}
			
			if ((_activeSet == null) && !string.IsNullOrEmpty(_config.GetString(Configuration.Emoticon.Active.Name, Configuration.Emoticon.Active.Default)))
			{
				if (_sets.Count > 0)
					_activeSet = _sets[0];
				else
					Log.Warn("No Emoticon Sets Found");
			}
		}
		
		public static void LoadCustomEmoticons()
		{
			_customEmoticons.Clear();
			
			foreach (IConfigurationSection accountsection in _config.Sections)
			{
				foreach (IConfigurationSection contactsection in accountsection.Sections)
				{
					foreach (IConfigurationSection emoticonsection in contactsection.Sections)
						_customEmoticons.Add(new CustomEmoticonInfo(emoticonsection));
				}
			}
		}
		
		public static List<IEmoticon> GetEmoticons(IAccount account, IContact dest, List<IEmoticon> known, bool outgoing)
		{
			List<IEmoticon> result = new List<IEmoticon>();
			
			// Add any emoticons we're given (received custom ones from the protocol)
			if (known != null)
				result.AddRange (known);
			
			// Add any custom emoticons
			if (outgoing)
			{
				foreach (CustomEmoticonInfo info in _customEmoticons)
				{
					if (info.Allow (account, dest))
						result.Add (info.Emoticon);
				}
			}
			
			// Add emoticons from the active set
			if (_activeSet != null)
				result.AddRange (_activeSet.Emoticons);
			
			return result;
		}
		
		public static List<IEmoticon> GetCustomEmoticons (IAccount account, IContact dest, List<IEmoticon> known)
		{
			List<IEmoticon> result = new List<IEmoticon>();
			
			// Add any custom emoticons
			foreach (CustomEmoticonInfo info in _customEmoticons)
			{
				if (info.Allow (account, dest))
					result.Add (info.Emoticon);
			}
			
			// Add any emoticons we're given (received custom ones from the protocol)
			if (known != null)
				result.AddRange (known);
			
			return result;
		}
		
		public static OrderedDictionary<string, IEmoticon> GetEmoticonDict(IAccount account, IContact dest, List<IEmoticon> known, bool outgoing)
		{
			if ((known == null) || (known.Count == 0))
			{
				//TODO: cache account/dest dicts based on a weak reference
				//if ((dest != null) && _cachedDicts.ContainsKey (dest))
				//	return _cachedDicts[dest];
				//if ((account != null) && _cachedDicts.ContainsKey (account))
				//	return _cachedDicts[account];
				
				if ((dest == null) && (account == null) && (_masterDict != null))
					return _masterDict;
			}
			
			List<IEmoticon> emoticons = GetEmoticons(account, dest, known, outgoing);
			OrderedDictionary<string, IEmoticon> result = new OrderedDictionary<string, IEmoticon>();
			
			foreach (IEmoticon emoticon in emoticons)
			{
				foreach (string equivalent in emoticon.Equivalents)
				{
					if (result.ContainsKey(equivalent))
						continue;
					
					int i;
					for (i = 0; i < result.Count; i++)
					{
						if (result.Keys[i].Length <= equivalent.Length)
							break;
					}
					
					result.Insert(i, equivalent, emoticon);
				}
			}

			if ((known == null) || (known.Count == 0))
			{
				if ((dest == null) && (account == null))
					_masterDict = result;
				//else
				//	_cachedDicts[(dest != null) ? dest as IEntity : account as IEntity] = result;
			}
			
            return result;
		}
		
		public static bool IsStandard(IEmoticon emoticon)
		{
			if (_activeSet == null)
				return false;
			
			return _activeSet.Emoticons.Contains(emoticon);
		}

		public static void CacheCustom(IAccount account, IContact contact, IEmoticon emoticon, string checksum)
		{
			string filename = GetCacheFilename(account, contact, checksum);
		
			if (File.Exists(filename))
				File.Delete(filename);
			
			BaseUtility.CreateDirectoryIfNeeded(Path.GetDirectoryName(filename));
			
			if ((!string.IsNullOrEmpty(emoticon.Filename)) && File.Exists(emoticon.Filename))
				File.Copy(emoticon.Filename, filename);
			else if ((emoticon.Data != null) && (emoticon.Data.Length > 0))
				File.WriteAllBytes(filename, emoticon.Data);
			else
				Log.Warn("Cannot cache emoticon with no content");
		}
		
		public static bool IsCached(IAccount account, IContact contact, string checksum)
		{
			return File.Exists(GetCacheFilename(account, contact, checksum));
		}
		
		public static string GetCacheFilename(IAccount account, IContact contact, string checksum)
		{
			string filename = Path.Combine(_cacheDir, "Custom");
			
			if (account != null)
				filename = Path.Combine(filename, account.UniqueIdentifier);
				
			if (contact != null)
				filename = Path.Combine(filename, contact.UniqueIdentifier);
				
			filename = Path.Combine(filename, BaseUtility.GetHashedName(checksum));
			
			return filename;
		}
		
		public static CustomEmoticonInfo AddCustom (string filename)
		{
			return AddCustom(filename,
							 Path.GetFileNameWithoutExtension (filename),
							 new string[] { string.Format (":{0}:", Path.GetFileNameWithoutExtension(filename)) },
							 "*",
							 "*");
		}
		
		public static CustomEmoticonInfo AddCustom (string filename, string name, string[] equivalents, string allowedAccount, string allowedDestination)
		{
			string customname = name;
			
			int i = 0;
			while (_config[allowedAccount][allowedDestination].ContainsSection(customname))
			{
				i++;
				customname = name + i.ToString();
			}
			
			IConfigurationSection config = _config[allowedAccount][allowedDestination][customname];
			
			string customfilename = Path.Combine(_customDir, Path.GetFileName(filename));
			
			i = 0;
			while (File.Exists(customfilename))
			{
				i++;
				customfilename = Path.Combine(_customDir, Path.GetFileNameWithoutExtension(filename) + i.ToString() + "." + Path.GetExtension(filename));
			}
			
			File.Copy(filename, customfilename);
			
			config.SetString("Filename", customfilename);
			
			string equivalentstr = string.Empty;
			
			foreach (string equivalent in equivalents)
				equivalentstr += equivalent + " ";
			
			config.SetString("Equivalents", equivalentstr.Trim());
			
			CustomEmoticonInfo info = new CustomEmoticonInfo(config);
			_customEmoticons.Add(info);
			
			return info;
		}
		
		public static void RemoveCustom(CustomEmoticonInfo info)
		{
			_customEmoticons.Remove(info);
			
			// Delete from custom dir
			File.Delete(info.Emoticon.Filename);
			
			info._config.Parent.RemoveSection(info._config.Name);
		}
		
		public static bool IsCustom (IEmoticon emot)
		{
			foreach (CustomEmoticonInfo info in _customEmoticons)
			{
				if (info.Emoticon == emot)
					return true;
			}
			
			return false;
		}
		
		static void OnExtensionChanged (object o, ExtensionEventArgs args)
		{
			if (args.PathChanged("/Galaxium/Themes/Emoticon"))
				LoadFactories();
		}
	}
}